package com.minimalist.basic.config.bucket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.time.Duration;

/**
 * @author 11987
 */
@Component
@Slf4j
public class DynamicRateLimitCalculator {
    @Value("${dynamic-rate-limit.base-limit}")
    private int baseLimit;

    @Value("${dynamic-rate-limit.cpu-threshold-high}")
    private double cpuThresholdHigh;

    @Value("${dynamic-rate-limit.cpu-threshold-medium}")
    private double cpuThresholdMedium;

    @Value("${dynamic-rate-limit.memory-threshold-high}")
    private double memoryThresholdHigh;

    @Value("${dynamic-rate-limit.memory-threshold-medium}")
    private double memoryThresholdMedium;

    public RateLimitConfig calculateLimit(SystemMetrics metrics) {
        int limit = baseLimit;
        limit = adjustLimitBasedOnCpu(limit, metrics.getCpuLoad());
        limit = adjustLimitBasedOnMemory(limit, metrics.getMemoryUsage());
        Duration refillDuration = calculateRefillDuration(metrics);
        return new RateLimitConfig(limit, refillDuration);
    }

    private int adjustLimitBasedOnCpu(int currentLimit, double cpuLoad) {
        if (cpuLoad > cpuThresholdHigh) {
            return (int) (currentLimit * 0.3);
        } else if (cpuLoad > cpuThresholdMedium) {
            return (int) (currentLimit * 0.6);
        }
        return currentLimit;
    }

    private int adjustLimitBasedOnMemory(int currentLimit, double memoryUsage) {
        if (memoryUsage > memoryThresholdHigh) {
            return (int) (currentLimit * 0.4);
        } else if (memoryUsage > memoryThresholdMedium) {
            return (int) (currentLimit * 0.7);
        }
        return currentLimit;
    }

    private Duration calculateRefillDuration(SystemMetrics metrics) {
        double maxLoad = Math.max(metrics.getCpuLoad(),
                metrics.getMemoryUsage());
        if (maxLoad > 0.8) {
            return Duration.ofMinutes(2);
        } else if (maxLoad > 0.5) {
            return Duration.ofMinutes(1);
        }
        return Duration.ofSeconds(30);
    }
}
